
// cCallback.h          (updated Version 3.0)

// by ElmueSoft  (www.elmue.de.vu  or  kickme.to/elmue)
// This class is platform independent and should theoretically run on all compilers
// This class is optimized for high speed


#if !defined(CALLBACK_ELMUESOFT_H)
#define CALLBACK_ELMUESOFT_H


#include "PreProcessor.h"
#include "assert.h"
#include "memory.h"

/*
________________________________________________________

Note 1:
--------

To call a function in C++ a function pointer is not enough like in the old days of C.
Additionally the instance pointer of the called class has to be passed.
The instance pointer (=this) points to the variable area of the instance.
The "this" pointer is passed with EVERY function call in C++ invisible for the programmer.


A function call in C++ like

CMyClass  i_MyClass;
i_MyClass.Init(8);

would be in C syntax like :

Init(i_MyClass, 8);

the compiler translates that to the machine code

PUSH        08h
LEA   ECX,  i_MyClass
CALL        CMyClass::Init

The only difference is that normal parameters are pushed on the stack
but the "this" pointer is passed in a processor register (Visual Studio 6 uses ECX)


________________________________________________________


Note 2:
-------

The compilers preprocessor is a PRIMITIVE text parser which does only text replacement
It is not capable to understand the simple line : #if (_ReturnType == void)

/////////////

_ReturnType Execute()
{
    #if (_ReturnType == void)
        CallBackFunction();
    #else
        return CallBackFunction();
    #endif
}


_ReturnType CallBackFunction()
{
    ......
}

\\\\\\\\\\\\\



So an awkward workaround with specialized templates and an unusable structure
is needed as provided in the file preprocessor.h

The above lines can be replaced by :


/////////////

typedef CLASS_SWITCH(IS_VOID(_ReturnType), UNUSABLE, _ReturnType)  _NewReturnType;

_NewReturnType Execute()
{
    return CallBackFunction();
}


_NewReturnType CallBackFunction()
{
    ......
}

\\\\\\\\\\\\\

Here _NewReturnType is defined as UNUSABLE (an empty structure) if _ReturnType is void.


________________________________________________________


Note 3:
--------

If CallBackFunction() does not return anything, because its return type is void the definition is modified to:

typedef unusable* (cCall0::*tFunction)();

This tells a lie to the compiler because it tells him that tFunction would return unusable*
which is not true. (In this case tFunction returns nothing)

But this is no problem, because the return value of a function is always returned
in a processor register. (Visual Studio 6 uses EAX) So when returning from tFunction
the content of EAX (which then is undefined) is put into unusable*
This doesn't matter because unusable* is never used afterwards !



++++++++++++++++++++++++++++++++++++++++++++++++++++++++

To understand the datails of what's going on here
I recommend to study the machine code in the ASSEMBLY listing.
Debug Build -> run into breakpoint 
Visual Studio 6.0:
press ALT + 8 or menu "View" -> "Debug Windows" -> "Disassembly"
Visual Studio  2005:
press CTRL + ALT + D or menu "Debug" -> "Windows" -> "Disassembly"

++++++++++++++++++++++++++++++++++++++++++++++++++++++++
*/


/***********************************************************
///////////////////   naming conventions   /////////////////
************************************************************


Using these conventions results in better readable code and less coding errors !


     cName  for class  definitions
     tName  for type   definitions
     eName  for enum   definitions
     kName  for struct definitions

    e_Name  for enum variables
    E_Name  for enum constant values

    i_Name  for instances of classes
    h_Name  for handles

    T_Name  for Templates

    s_Name  for strings
    r_Name  for return values
    f_Name  for function pointers
    k_Name  for contructs (struct)

    b_Name      bool  1 Bit

   s8_Name    signed  8 Bit (char)
  s16_Name    signed 16 Bit (SHORT, WCHAR)
  s32_Name    signed 32 Bit (LONG, int)
  s64_Name    signed 64 Bit (LONGLONG)

   u8_Name  unsigned  8 Bit (BYTE)
  u16_Name  unsigned 16 bit (WORD)
  u32_Name  unsigned 32 Bit (DWORD, UINT)
  u64_Name  unsigned 64 Bit (ULONGLONG)

    d_Name  for double

  ----------------

    m_Name  for member variables of a class (e.g. ms32_Name for int member variable)
    g_Name  for global variables            (e.g. gu16_Name for global WORD)
    p_Name  for pointer                     (e.g.   ps_Name pointer to string)
   pp_Name  for pointer to pointer          (e.g.  ppd_Name **pointer to double)


 *******************************************************************
 ***              Callback with 0 up to 5 arguments              ***
 ***                      and any return type                    ***
 *******************************************************************/


template <class tRet, class tArg1=UNUSABLE,
                      class tArg2=UNUSABLE,
                      class tArg3=UNUSABLE,
                      class tArg4=UNUSABLE,
                      class tArg5=UNUSABLE>
class cCallBase
{
public:
    // if callback function returns void: set return type to unusable
    typedef typename CLASS_SWITCH(IS_VOID(tRet), UNUSABLE, tRet)  ret_type;

    // the callbacks to static functions
    typedef ret_type (*tFunctionS0)();
    typedef ret_type (*tFunctionS1)(tArg1);
    typedef ret_type (*tFunctionS2)(tArg1, tArg2);
    typedef ret_type (*tFunctionS3)(tArg1, tArg2, tArg3);
    typedef ret_type (*tFunctionS4)(tArg1, tArg2, tArg3, tArg4);
    typedef ret_type (*tFunctionS5)(tArg1, tArg2, tArg3, tArg4, tArg5);

    // storage of callback to static functions
    struct kDataS
    {
        int s32_Params; // number of valid arguments
        union
        {
            tFunctionS0  S0;
            tFunctionS1  S1;
            tFunctionS2  S2;
            tFunctionS3  S3;
            tFunctionS4  S4;
            tFunctionS5  S5;
        } f_Func;
    };

    // the callbacks to member functions of a C++ class
    typedef ret_type (cCallBase::*tFunctionM0)();
    typedef ret_type (cCallBase::*tFunctionM1)(tArg1);
    typedef ret_type (cCallBase::*tFunctionM2)(tArg1, tArg2);
    typedef ret_type (cCallBase::*tFunctionM3)(tArg1, tArg2, tArg3);
    typedef ret_type (cCallBase::*tFunctionM4)(tArg1, tArg2, tArg3, tArg4);
    typedef ret_type (cCallBase::*tFunctionM5)(tArg1, tArg2, tArg3, tArg4, tArg5);

    // storage of callback to class member functions
    struct kDataM
    {
        int       s32_Params; // number of valid arguments
        cCallBase *pi_Inst;
        union
        {
            tFunctionM0  M0;
            tFunctionM1  M1;
            tFunctionM2  M2;
            tFunctionM3  M3;
            tFunctionM4  M4;
            tFunctionM5  M5;
        } f_Func;
    };

    ret_type Execute()
    {
        assert(0); // this is never executed (function is overridden)
        return ret_type();
    }

    ret_type Execute(tArg1 Arg1)
    {
        assert(0); // this is never executed (function is overridden)
        return ret_type();
    }

    ret_type Execute(tArg1 Arg1, tArg2 Arg2)
    {
        assert(0); // this is never executed (function is overridden)
        return ret_type();
    }

    ret_type Execute(tArg1 Arg1, tArg2 Arg2, tArg3 Arg3)
    {
        assert(0); // this is never executed (function is overridden)
        return ret_type();
    }

    ret_type Execute(tArg1 Arg1, tArg2 Arg2, tArg3 Arg3, tArg4 Arg4)
    {
        assert(0); // this is never executed (function is overridden)
        return ret_type();
    }

    ret_type Execute(tArg1 Arg1, tArg2 Arg2, tArg3 Arg3, tArg4 Arg4, tArg5 Arg5)
    {
        assert(0); // this is never executed (function is overridden)
        return ret_type();
    }

protected:
    kDataS k_DataS;
    kDataM k_DataM;
};

template <class tRet, class tArg1=UNUSABLE,
                      class tArg2=UNUSABLE,
                      class tArg3=UNUSABLE,
                      class tArg4=UNUSABLE,
                      class tArg5=UNUSABLE>

class cCall : public virtual cCallBase<tRet, tArg1, tArg2, tArg3, tArg4, tArg5>
{
public:
    // the callbacks to functions in a virtually derived class
    typedef ret_type (cCall::*tFunctionV0)();
    typedef ret_type (cCall::*tFunctionV1)(tArg1);
    typedef ret_type (cCall::*tFunctionV2)(tArg1, tArg2);
    typedef ret_type (cCall::*tFunctionV3)(tArg1, tArg2, tArg3);
    typedef ret_type (cCall::*tFunctionV4)(tArg1, tArg2, tArg3, tArg4);
    typedef ret_type (cCall::*tFunctionV5)(tArg1, tArg2, tArg3, tArg4, tArg5);

    // storage of virtual callback
    struct kDataV
    {
        int   s32_Params; // number of valid arguments
        cCall *pi_Inst;
        union
        {
            tFunctionV0  V0;
            tFunctionV1  V1;
            tFunctionV2  V2;
            tFunctionV3  V3;
            tFunctionV4  V4;
            tFunctionV5  V5;
        } f_Func;
    };

    cCall()
    {
        memset(&k_DataM, 0, sizeof(k_DataM));
        memset(&k_DataV, 0, sizeof(k_DataV));
        memset(&k_DataS, 0, sizeof(k_DataS));
    }

    // Set external Callback or clear Callback
    cCall& operator=(cCall<tRet, tArg1, tArg2, tArg3, tArg4, tArg5> &i_Callback)
    {
        if (&i_Callback)
        {
            k_DataV = i_Callback.k_DataV;
            k_DataM = i_Callback.k_DataM;
            k_DataS = i_Callback.k_DataS;
        }
        else cCall();
        return *this;
    }

    bool Valid()
    {
        return (k_DataM.f_Func.M0 && k_DataM.pi_Inst) || 
               (k_DataV.f_Func.V0 && k_DataV.pi_Inst) ||
                k_DataS.f_Func.S0;
    }

    ret_type Execute()
    {
        if (Check('S', 0)) return                   (k_DataS.f_Func.S0)();
        if (Check('M', 0)) return (k_DataM.pi_Inst->*k_DataM.f_Func.M0)();
        if (Check('V', 0)) return (k_DataV.pi_Inst->*k_DataV.f_Func.V0)();

        assert(0); // callback is uninitialized or too few arguments passed to Execute() !
        return ret_type();
    }

    ret_type Execute(tArg1 Arg1)
    {
        if (Check('S', 1)) return                   (k_DataS.f_Func.S1)(Arg1);
        if (Check('M', 1)) return (k_DataM.pi_Inst->*k_DataM.f_Func.M1)(Arg1);
        if (Check('V', 1)) return (k_DataV.pi_Inst->*k_DataV.f_Func.V1)(Arg1);

        assert(0); // callback is uninitialized or too few arguments passed to Execute() !
        return ret_type();
    }

    ret_type Execute(tArg1 Arg1, tArg2 Arg2)
    {
        if (Check('S', 2)) return                   (k_DataS.f_Func.S2)(Arg1, Arg2);
        if (Check('M', 2)) return (k_DataM.pi_Inst->*k_DataM.f_Func.M2)(Arg1, Arg2);
        if (Check('V', 2)) return (k_DataV.pi_Inst->*k_DataV.f_Func.V2)(Arg1, Arg2);

        assert(0); // callback is uninitialized or too few arguments passed to Execute() !
        return ret_type();
    }

    ret_type Execute(tArg1 Arg1, tArg2 Arg2, tArg3 Arg3)
    {
        if (Check('S', 3)) return                   (k_DataS.f_Func.S3)(Arg1, Arg2, Arg3);
        if (Check('M', 3)) return (k_DataM.pi_Inst->*k_DataM.f_Func.M3)(Arg1, Arg2, Arg3);
        if (Check('V', 3)) return (k_DataV.pi_Inst->*k_DataV.f_Func.V3)(Arg1, Arg2, Arg3);

        assert(0); // callback is uninitialized or too few arguments passed to Execute() !
        return ret_type();
    }

    ret_type Execute(tArg1 Arg1, tArg2 Arg2, tArg3 Arg3, tArg4 Arg4)
    {
        if (Check('S', 4)) return                   (k_DataS.f_Func.S4)(Arg1, Arg2, Arg3, Arg4);
        if (Check('M', 4)) return (k_DataM.pi_Inst->*k_DataM.f_Func.M4)(Arg1, Arg2, Arg3, Arg4);
        if (Check('V', 4)) return (k_DataV.pi_Inst->*k_DataV.f_Func.V4)(Arg1, Arg2, Arg3, Arg4);

        assert(0); // callback is uninitialized or too few arguments passed to Execute() !
        return ret_type();
    }

    ret_type Execute(tArg1 Arg1, tArg2 Arg2, tArg3 Arg3, tArg4 Arg4, tArg5 Arg5)
    {
        if (Check('S', 5)) return                   (k_DataS.f_Func.S5)(Arg1, Arg2, Arg3, Arg4, Arg5);
        if (Check('M', 5)) return (k_DataM.pi_Inst->*k_DataM.f_Func.M5)(Arg1, Arg2, Arg3, Arg4, Arg5);
        if (Check('V', 5)) return (k_DataV.pi_Inst->*k_DataV.f_Func.V5)(Arg1, Arg2, Arg3, Arg4, Arg5);

        assert(0); // callback is uninitialized!
        return ret_type();
    }

protected:
    kDataV k_DataV;

    bool Check(char s8_Type, int s32_ParamCount)
    {
        switch (s8_Type)
        {
        case 'S': // static
            return (k_DataS.f_Func.S0 && k_DataS.s32_Params == s32_ParamCount);
        case 'M': // member
            return (k_DataM.f_Func.M0 && k_DataM.s32_Params == s32_ParamCount && k_DataM.pi_Inst);
        case 'V': // virtual
            return (k_DataV.f_Func.V0 && k_DataV.s32_Params == s32_ParamCount && k_DataV.pi_Inst);
        default:
            return false;
        }
    }
};

// use this for callbacks to C++ member functions
template <class cInstance, class tRet, class tArg1=UNUSABLE,
                                       class tArg2=UNUSABLE,
                                       class tArg3=UNUSABLE,
                                       class tArg4=UNUSABLE,
                                       class tArg5=UNUSABLE>
class cCallGen : public cCall<tRet, tArg1, tArg2, tArg3, tArg4, tArg5>
{
public:
    typedef tRet (cInstance::*tFunction0)();
    typedef tRet (cInstance::*tFunction1)(tArg1);
    typedef tRet (cInstance::*tFunction2)(tArg1, tArg2);
    typedef tRet (cInstance::*tFunction3)(tArg1, tArg2, tArg3);
    typedef tRet (cInstance::*tFunction4)(tArg1, tArg2, tArg3, tArg4);
    typedef tRet (cInstance::*tFunction5)(tArg1, tArg2, tArg3, tArg4, tArg5);

    // Chose the correct argument count
    typedef typename CLASS_SWITCH(IS_USABLE(tArg5), tFunction5, tFunction4)  tFunctionA;
    typedef typename CLASS_SWITCH(IS_USABLE(tArg4), tFunctionA, tFunction3)  tFunctionB;
    typedef typename CLASS_SWITCH(IS_USABLE(tArg3), tFunctionB, tFunction2)  tFunctionC;
    typedef typename CLASS_SWITCH(IS_USABLE(tArg2), tFunctionC, tFunction1)  tFunctionM;
    typedef typename CLASS_SWITCH(IS_USABLE(tArg1), tFunctionM, tFunction0)  tFunction;

    struct kRetFunc
    {
        int     s32_Params; // number of valid arguments
        void    *pi_Inst;
        tFunction f_Func;
    };

    // Constructor
    cCallGen (cInstance *pi_Instance, tFunction f_Func)
    {
        int s32_Params = IS_USABLE(tArg1) + IS_USABLE(tArg2) + IS_USABLE(tArg3) + IS_USABLE(tArg4) + IS_USABLE(tArg5);

        kRetFunc k_RetFunc = { s32_Params, pi_Instance, f_Func };

        if (sizeof(k_RetFunc) == sizeof(k_DataV))
        {
            // sizeof(f_Func) = 12
            memcpy(&k_DataV, &k_RetFunc, sizeof(k_DataV));
        }
        else if (sizeof(k_RetFunc) == sizeof(k_DataM))
        {
            // sizeof(f_Func) = 4
            memcpy(&k_DataM, &k_RetFunc, sizeof(k_DataM));
        }
        else
        {
            assert(0); // Unknown compiler error
        }
    }
};

// use this for callbacks to static C++ functions
template <class tRet, class tArg1=UNUSABLE,
                      class tArg2=UNUSABLE,
                      class tArg3=UNUSABLE,
                      class tArg4=UNUSABLE,
                      class tArg5=UNUSABLE>
class cCallGenS : public cCall<tRet, tArg1, tArg2, tArg3, tArg4, tArg5>
{
public:
    typedef tRet (*tFunction0)();
    typedef tRet (*tFunction1)(tArg1);
    typedef tRet (*tFunction2)(tArg1, tArg2);
    typedef tRet (*tFunction3)(tArg1, tArg2, tArg3);
    typedef tRet (*tFunction4)(tArg1, tArg2, tArg3, tArg4);
    typedef tRet (*tFunction5)(tArg1, tArg2, tArg3, tArg4, tArg5);

    // Chose the correct argument count
    typedef typename CLASS_SWITCH(IS_USABLE(tArg5), tFunction5, tFunction4)  tFunctionA;
    typedef typename CLASS_SWITCH(IS_USABLE(tArg4), tFunctionA, tFunction3)  tFunctionB;
    typedef typename CLASS_SWITCH(IS_USABLE(tArg3), tFunctionB, tFunction2)  tFunctionC;
    typedef typename CLASS_SWITCH(IS_USABLE(tArg2), tFunctionC, tFunction1)  tFunctionM;
    typedef typename CLASS_SWITCH(IS_USABLE(tArg1), tFunctionM, tFunction0)  tFunction;

    struct kRetFunc
    {
        int     s32_Params; // number of valid arguments
        tFunction f_Func;
    };

    // Constructor
    cCallGenS (tFunction f_Func)
    {
        int s32_Params = IS_USABLE(tArg1) + IS_USABLE(tArg2) + IS_USABLE(tArg3) + IS_USABLE(tArg4) + IS_USABLE(tArg5);

        kRetFunc k_RetFunc = { s32_Params, f_Func };

        // sizeof(f_Func) = 4
        memcpy(&k_DataS, &k_RetFunc, sizeof(k_DataS));
    }
};


#endif // CALLBACK_ELMUESOFT_H

